library(ggpubr)
Loading required package: magrittr

Attaching package: ‘magrittr’

The following object is masked from ‘package:purrr’:

    set_names

The following object is masked from ‘package:tidyr’:

    extract


Attaching package: ‘ggpubr’

The following object is masked from ‘package:cowplot’:

    get_legend
f74.rna.seu@meta.data <- f74.rna.seu@meta.data %>%
  rownames_to_column("cell") %>%
  left_join(annotation.df) %>%
  column_to_rownames("cell")
Joining, by = "cell"

First I have made the snap file from the cellranger fragments.tsv file. Script is multiOmic_benchmark/preprocess/fragments2snap.sh.

Following integration vignette

Barcode selection

Filtering based on number of reads per cell and ratio of fragments that are within promoters

barcode.ls = lapply(seq(snap.files), function(i){
  barcodes = barcode.ls[[i]];
  idx = which(
      barcodes$logUMI >= cutoff.logUMI.low[i] & 
      barcodes$logUMI <= cutoff.logUMI.high[i] & 
      barcodes$promoter_ratio >= cutoff.FRIP.low[i] &
      barcodes$promoter_ratio <= cutoff.FRIP.high[i]
  );
  barcodes[idx,]
});
x.sp.ls = lapply(seq(snap.files), function(i){
  barcodes = barcode.ls[[i]];
  x.sp = x.sp.ls[[i]];
  barcode.shared = intersect(x.sp@barcode, barcodes$barcode);
  x.sp = x.sp[match(barcode.shared, x.sp@barcode),];
  barcodes = barcodes[match(barcode.shared, barcodes$barcode),];
  x.sp@metaData = barcodes;
  x.sp
})
names(x.sp.ls) = sample.names;
x.sp = Reduce(snapRbind, x.sp.ls);
x.sp@metaData["sample"] = x.sp@sample;
x.sp
number of barcodes: 6043
number of bins: 0
number of genes: 0
number of peaks: 0
number of motifs: 0

Add cell-by-bin matrix

x.sp = addBmatToSnap(x.sp, bin.size = 5000)
Epoch: reading cell-bin count matrix session ...

Binarize matrix

Some items in the count matrix have abnormally high coverage perhaps due to the alignment errors. Therefore, we next remove top 0.1% items in the count matrix and then convert the remaining non-zero values to 1.

x.sp = makeBinary(x.sp, mat="bmat")
x.sp
number of barcodes: 6043
number of bins: 651791
number of genes: 0
number of peaks: 0
number of motifs: 0

Filter bins

Filter out bins overlapping w ENCODE blacklist

black_list = read.table("~/annotations/hg38.blacklist.bed.gz")
black_list.gr = GRanges(
  black_list[,1], 
  IRanges(black_list[,2], black_list[,3])
);
idy = queryHits(
  findOverlaps(x.sp@feature, black_list.gr)
);
if(length(idy) > 0){
  x.sp = x.sp[,-idy, mat="bmat"];
};
x.sp
number of barcodes: 6043
number of bins: 651765
number of genes: 0
number of peaks: 0
number of motifs: 0

Exclude bad chromosomes

chr.exclude = seqlevels(x.sp@feature)[grep("random|chrM", seqlevels(x.sp@feature))]
idy = grep(paste(chr.exclude, collapse="|"), x.sp@feature)
if(length(idy) > 0){
  x.sp = x.sp[,-idy, mat="bmat"]
}
x.sp
number of barcodes: 6043
number of bins: 650342
number of genes: 0
number of peaks: 0
number of motifs: 0

remove the top 5% bins that overlap with invariant features such as the house keeping gene promoters

bin.cov = log10(Matrix::colSums(x.sp@bmat)+1)
# bin.cov = Matrix::colSums(x.sp@bmat)
hist(
  bin.cov[bin.cov > 0], 
  xlab="log10(bin cov)", 
  main="log10(Bin Cov)", 
  col="lightblue", 
  # xlim=c(0, 5),
  breaks=100
);

bin.cutoff = quantile(bin.cov[bin.cov > 0], 0.95)
idy = which(bin.cov <= bin.cutoff & bin.cov > 0)
x.sp = x.sp[, idy, mat="bmat"];
x.sp
number of barcodes: 6043
number of bins: 539584
number of genes: 0
number of peaks: 0
number of motifs: 0

Remove any cells of bin coverage less than 1,000. The rational behind this is that some cells may have high number of unique fragments but end up with low bin coverage after filtering. This step is optional but highly recommanded.

idx = which(Matrix::rowSums(x.sp@bmat) > 1000);
x.sp = x.sp[idx,];
x.sp
number of barcodes: 5793
number of bins: 539584
number of genes: 0
number of peaks: 0
number of motifs: 0

Dimensionality reduction

Uses diffusion map algorithm w sampling technique to make it fast.

## Sample 100 cells as landmarks 
row.covs.dens <- density(
  x = x.sp@metaData[,"logUMI"], 
  bw = 'nrd', adjust = 1
)
sampling_prob <- 1 / (approx(x = row.covs.dens$x, y = row.covs.dens$y, xout = x.sp@metaData[,"logUMI"])$y + .Machine$double.eps);
set.seed(1)
idx.landmark.ds <- sort(sample(x = seq(nrow(x.sp)), size = 1000, prob = sampling_prob))
## Split between landmark and query cells
x.landmark.sp = x.sp[idx.landmark.ds,];
x.query.sp = x.sp[-idx.landmark.ds,];
## Run diffusion map on landmark
x.landmark.sp = runDiffusionMaps(
  obj= x.landmark.sp,
  input.mat="bmat", 
  num.eigs=50
);
Epoch: checking the inputs ...
Epoch: computing jaccard similarity matrix ...
Epoch: fitting regression model ...
Epoch: performing normalization ...
Epoch: computing eigen decomposition ...
Epoch: Done
x.landmark.sp@metaData$landmark = 1;
## Project query cells
x.query.sp = runDiffusionMapsExtension(
  obj1=x.landmark.sp, 
  obj2=x.query.sp,
  input.mat="bmat"
)
Epoch: checking the inputs ...
Epoch: computing jaccard similarity matrix ...
Epoch: performing normalization ...
Epoch: projecting query cells to the reference ...
Epoch: Done
x.query.sp@metaData$landmark = 0;
## Combine 
x.sp = snapRbind(x.landmark.sp, x.query.sp);
'rBind' is deprecated.
 Since R version 3.2.0, base's rbind() should work fine with S4 objects
x.sp = x.sp[order(x.sp@metaData[,"sample"])]; #IMPORTANT

To determine significant diffusion components: > We use an ad hoc method by simply looking at a pairwise plot and select the number of eigen vectors that the scatter plot starts looking like a blob. In the below example, we choose the first 15 eigen vectors.

plotDimReductPW(
    obj=x.sp, 
    eigs.dims=1:50,
    point.size=0.3,
    point.color="grey",
    point.shape=19,
    point.alpha=0.6,
    down.sample=5000,
    pdf.file.name=NULL, 
    pdf.height=7, 
    pdf.width=7
  );

Clustering and visualization

x.sp=runCluster(
    obj=x.sp,
    tmp.folder=tempdir(),
    louvain.lib="leiden",
    seed.use=10,
    resolution=0.7
  );
Epoch: checking input parameters
Epoch: finding clusters using leiden

Visualization

Make gene matrix

saveRDS("~/my_data/cellranger-atac110_count_30439_WSSS8038360_GRCh38-1_1_0.snapATAC.RDS")
Error in saveRDS("~/my_data/cellranger-atac110_count_30439_WSSS8038360_GRCh38-1_1_0.snapATAC.RDS") : 
  'file' must be non-empty string

Repeat visualization on gmat

FeaturePlot(f74.seu, features = c('PTPRC','CD4','CD8A','CD8B','CD79A','FOXN1','EPCAM','PDGFRA','GNG4', 'FOXP3','RAG1','RAG2','NKG7','CCR7'), reduction = "umap.snap")
All cells have the same value (0) of CD79A.All cells have the same value (0) of RAG2.All cells have the same value (0) of NKG7.All cells have the same value (0) of CCR7.
DimHeatmap(f74.seu, dims = 1:6, balanced = TRUE,ncol = 1)

31 Oct 2019 09:28:40 [rsession-] CLIENT EXCEPTION (rsession-jovyan): (TypeError) : Cannot read property 'a' of null;
org/rstudio/studio/client/workbench/views/source/editors/text/ChunkPlotPage.java#52::execute
org/rstudio/studio/client/workbench/views/source/editors/text/ChunkPlotWidget.java#49::onBrowserEvent
com/google/gwt/user/client/DOM.java#1414::dispatchEvent
com/google/gwt/user/client/impl/DOMImplStandard.java#312::dispatchEvent
com/google/gwt/user/client/impl/DOMImplStandard.java#334::dispatchUnhandledEvent
com/google/gwt/core/client/impl/Impl.java#244::apply
com/google/gwt/core/client/impl/Impl.java#283::entry0
rstudio-0.js#-1::eval
Client-ID: 7beb3877-891d-4e16-98d5-f8cca1f72d5d
User-Agent: Mozilla/5.0 (Macintosh  Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36

Differential analysis

Expression of markers in RNA data

map(1:10, ~ plot_cluster_marker_ATACvsRNA(.x))
All cells have the same value (0) of MIR4300HG.All cells have the same value (0) of NOS1.All cells have the same value (0) of RPH3A.Cannot convert object of class listggarrange into a grob.All cells have the same value (0) of C5orf66-AS2.All cells have the same value (0) of CYP3A43.
[[1]]

[[2]]

[[3]]

[[4]]

[[5]]

[[6]]

[[7]]

[[8]]

[[9]]

[[10]]

Embedding on most variable genes in ATAC (not in gene expression)

Thoughts

LS0tCnRpdGxlOiAiU25hcCBBVEFDIGRhdGEgcHJvY2Vzc2luZyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3J9CnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyh7CiAgbGlicmFyeShTbmFwQVRBQykKICBsaWJyYXJ5KGxlaWRlbikKICBsaWJyYXJ5KHVtYXApCiAgbGlicmFyeShHZW5vbWljUmFuZ2VzKQogIGxpYnJhcnkoem9vKQogIGxpYnJhcnkodGlkeXZlcnNlKQogIGxpYnJhcnkoY293cGxvdCkKICBsaWJyYXJ5KGdncHVicikKfSkKc291cmNlKCJ+L211bHRpT21pY19iZW5jaG1hcmsvcHJlcHJvY2Vzcy9zZWxlY3RGZWF0dXJlcy5SIikKYGBgCmBgYHtyfQojIyBMb2FkIGFuZCBlbWJlZCBSTkEgZGF0YQpmNzQuc2NlLmxpc3QgPC0gcmVhZFJEUygifi9teV9kYXRhL2ludGVncmF0ZWRfdGh5bXVzL0Y3NF9TQ0VsaXN0XzIwMTkxMDE3LlJEUyIpCmY3NC5ybmEgPC0gZjc0LnNjZS5saXN0JFJOQQoKZjc0LnJuYS5zZXUgPC0gYXMuU2V1cmF0KGY3NC5ybmEsIHZlcmJvc2U9RkFMU0UpCmludGVncmF0ZV9mZWF0dXJlcyA8LSBIVkdfU2V1cmF0KGY3NC5ybmEsIG5mZWF0dXJlcyA9IDMwMDApClZhcmlhYmxlRmVhdHVyZXMoZjc0LnJuYS5zZXUpIDwtIGludGVncmF0ZV9mZWF0dXJlcwpmNzQucm5hLnNldSA8LSBTY2FsZURhdGEoZjc0LnJuYS5zZXUsIGRvLmNlbnRlciA9IFRSVUUsIHZlcmJvc2U9RkFMU0UpCmY3NC5ybmEuc2V1IDwtIFJ1blBDQShmNzQucm5hLnNldSkgCmY3NC5ybmEuc2V1IDwtIFJ1blVNQVAoZjc0LnJuYS5zZXUsIHJlZHVjdGlvbiA9ICJwY2EiLCBkaW1zPTE6NTApIAoKIyMgQWRkIGNlbGwgdHlwZSBhbm5vdGF0aW9uCmFubm90YXRpb24uZGYgPC0gcmVhZC5jc3YoIn4vbXlfZGF0YS9GNzRfUk5BX29icy5jc3YiKQphbm5vdGF0aW9uLmRmIDwtIGFubm90YXRpb24uZGYgJT4lCiAgbXV0YXRlKGNlbGw9c3RyX3JlbW92ZShhcy5jaGFyYWN0ZXIoWCksICJGNzRfMV8iKSAlPiUgc3RyX2MoaWZlbHNlKGJhdGNoPT0wLCdfMScsICJfMiIpKSkgCgpmNzQucm5hLnNldUBtZXRhLmRhdGEgPC0gZjc0LnJuYS5zZXVAbWV0YS5kYXRhICU+JQogIHJvd25hbWVzX3RvX2NvbHVtbigiY2VsbCIpICU+JQogIGxlZnRfam9pbihhbm5vdGF0aW9uLmRmKSAlPiUKICBjb2x1bW5fdG9fcm93bmFtZXMoImNlbGwiKQoKYGBgCgoKRmlyc3QgSSBoYXZlIG1hZGUgdGhlIHNuYXAgZmlsZSBmcm9tIHRoZSBjZWxscmFuZ2VyIGBmcmFnbWVudHMudHN2YCBmaWxlLiBTY3JpcHQgaXMgYG11bHRpT21pY19iZW5jaG1hcmsvcHJlcHJvY2Vzcy9mcmFnbWVudHMyc25hcC5zaGAuCgpGb2xsb3dpbmcgW2ludGVncmF0aW9uIHZpZ25ldHRlXShodHRwczovL2dpdGh1Yi5jb20vcjNmYW5nL1NuYXBBVEFDL2Jsb2IvbWFzdGVyL2V4YW1wbGVzLzEwWF9QQk1DXzE1Sy9SRUFETUUubWQpIAoKIyMjIEJhcmNvZGUgc2VsZWN0aW9uCkZpbHRlcmluZyBiYXNlZCBvbiBudW1iZXIgb2YgcmVhZHMgcGVyIGNlbGwgYW5kIHJhdGlvIG9mIGZyYWdtZW50cyB0aGF0IGFyZSB3aXRoaW4gcHJvbW90ZXJzCmBgYHtyfQpzbmFwLmZpbGVzIDwtICJ+L215X2RhdGEvY2VsbHJhbmdlci1hdGFjMTEwX2NvdW50XzMwNDM5X1dTU1M4MDM4MzYwX0dSQ2gzOC0xXzFfMC5zbmFwIgpzYW1wbGUubmFtZXMgPC0gIkY3NCIKYmFyY29kZS5maWxlcyA8LSAnfi9teV9kYXRhL3NpbmdsZWNlbGwuY3N2JwoKeC5zcC5scyA8LSBsaXN0KGNyZWF0ZVNuYXAoc25hcC5maWxlcywgc2FtcGxlLm5hbWVzKSkKbmFtZXMoeC5zcC5scykgPSBzYW1wbGUubmFtZXMKeC5zcC5scwoKYmFyY29kZS5scyA9IGxhcHBseShzZXEoc25hcC5maWxlcyksIGZ1bmN0aW9uKGkpewogICAgYmFyY29kZXMgPSByZWFkLmNzdigKICAgICAgICBiYXJjb2RlLmZpbGVzW2ldLCAKICAgICAgICBoZWFkPVRSVUUKICAgICk7CiAgICAjIHJlbW92ZSBOTyBCQVJPQ0RFIGxpbmUKICAgIGJhcmNvZGVzID0gYmFyY29kZXNbMjpucm93KGJhcmNvZGVzKSxdOwogICAgYmFyY29kZXMkbG9nVU1JID0gbG9nMTAoYmFyY29kZXMkcGFzc2VkX2ZpbHRlcnMgKyAxKTsKICAgIGJhcmNvZGVzJHByb21vdGVyX3JhdGlvID0gKGJhcmNvZGVzJHByb21vdGVyX3JlZ2lvbl9mcmFnbWVudHMrMSkgLyAoYmFyY29kZXMkcGFzc2VkX2ZpbHRlcnMgKyAxKTsKICAgIGJhcmNvZGVzCiAgfSkKCnBsb3RzID0gbGFwcGx5KHNlcShzbmFwLmZpbGVzKSwgZnVuY3Rpb24oaSl7CiAgICBwMSA9IGdncGxvdCgKICAgICAgICBiYXJjb2RlLmxzW1tpXV0sIAogICAgICAgIGFlcyh4PWxvZ1VNSSwgeT1wcm9tb3Rlcl9yYXRpbykpICsgCiAgICAgICAgZ2VvbV9wb2ludChzaXplPTAuMywgY29sPSJncmV5IikgKwogICAgICAgIHRoZW1lX2NsYXNzaWMoKQkrCiAgICAgICAgZ2d0aXRsZShzYW1wbGUubmFtZXNbW2ldXSkgKwogICAgICAgIHlsaW0oMCwgMSkgKyB4bGltKDAsIDYpICsgCiAgICAgICAgbGFicyh4ID0gImxvZzEwKGNvdW50cykiLCB5PSJwcm9tb3RlciByYXRpbyIpCiAgICAgICAgcDEKICAgIH0pCgojIyBTZWxlY3QgYW5kIHZpeiBjdXRvZmZzCmN1dG9mZi5sb2dVTUkubG93ID0gMy4zCmN1dG9mZi5sb2dVTUkuaGlnaCA9IDQuOApjdXRvZmYuRlJJUC5sb3cgPSAwLjI1CmN1dG9mZi5GUklQLmhpZ2ggPSAwLjY1CgoKcGxvdHNbWzFdXSArIAogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMoY3V0b2ZmLmxvZ1VNSS5sb3dbMV0sY3V0b2ZmLmxvZ1VNSS5oaWdoWzFdKSwgbGluZXR5cGU9MikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IGMoY3V0b2ZmLkZSSVAubG93WzFdLGN1dG9mZi5GUklQLmhpZ2hbMV0pLCBsaW5ldHlwZT0yKQpgYGAKYGBge3J9CmJhcmNvZGUubHMgPSBsYXBwbHkoc2VxKHNuYXAuZmlsZXMpLCBmdW5jdGlvbihpKXsKICBiYXJjb2RlcyA9IGJhcmNvZGUubHNbW2ldXTsKICBpZHggPSB3aGljaCgKICAgICAgYmFyY29kZXMkbG9nVU1JID49IGN1dG9mZi5sb2dVTUkubG93W2ldICYgCiAgICAgIGJhcmNvZGVzJGxvZ1VNSSA8PSBjdXRvZmYubG9nVU1JLmhpZ2hbaV0gJiAKICAgICAgYmFyY29kZXMkcHJvbW90ZXJfcmF0aW8gPj0gY3V0b2ZmLkZSSVAubG93W2ldICYKICAgICAgYmFyY29kZXMkcHJvbW90ZXJfcmF0aW8gPD0gY3V0b2ZmLkZSSVAuaGlnaFtpXQogICk7CiAgYmFyY29kZXNbaWR4LF0KfSk7Cnguc3AubHMgPSBsYXBwbHkoc2VxKHNuYXAuZmlsZXMpLCBmdW5jdGlvbihpKXsKICBiYXJjb2RlcyA9IGJhcmNvZGUubHNbW2ldXTsKICB4LnNwID0geC5zcC5sc1tbaV1dOwogIGJhcmNvZGUuc2hhcmVkID0gaW50ZXJzZWN0KHguc3BAYmFyY29kZSwgYmFyY29kZXMkYmFyY29kZSk7CiAgeC5zcCA9IHguc3BbbWF0Y2goYmFyY29kZS5zaGFyZWQsIHguc3BAYmFyY29kZSksXTsKICBiYXJjb2RlcyA9IGJhcmNvZGVzW21hdGNoKGJhcmNvZGUuc2hhcmVkLCBiYXJjb2RlcyRiYXJjb2RlKSxdOwogIHguc3BAbWV0YURhdGEgPSBiYXJjb2RlczsKICB4LnNwCn0pCm5hbWVzKHguc3AubHMpID0gc2FtcGxlLm5hbWVzOwp4LnNwID0gUmVkdWNlKHNuYXBSYmluZCwgeC5zcC5scyk7Cnguc3BAbWV0YURhdGFbInNhbXBsZSJdID0geC5zcEBzYW1wbGU7Cnguc3AKYGBgCgojIyMgQWRkIGNlbGwtYnktYmluIG1hdHJpeApgYGB7cn0KeC5zcCA9IGFkZEJtYXRUb1NuYXAoeC5zcCwgYmluLnNpemUgPSA1MDAwKQpgYGAKIyMgQmluYXJpemUgbWF0cml4ClNvbWUgaXRlbXMgaW4gdGhlIGNvdW50IG1hdHJpeCBoYXZlIGFibm9ybWFsbHkgaGlnaCBjb3ZlcmFnZSBwZXJoYXBzIGR1ZSB0byB0aGUgYWxpZ25tZW50IGVycm9ycy4gVGhlcmVmb3JlLCB3ZSBuZXh0IHJlbW92ZSB0b3AgMC4xJSBpdGVtcyBpbiB0aGUgY291bnQgbWF0cml4IGFuZCB0aGVuIGNvbnZlcnQgdGhlIHJlbWFpbmluZyBub24temVybyB2YWx1ZXMgdG8gMS4KYGBge3J9Cnguc3AgPSBtYWtlQmluYXJ5KHguc3AsIG1hdD0iYm1hdCIpCnguc3AKYGBgCgojIyBGaWx0ZXIgYmlucwpGaWx0ZXIgb3V0IGJpbnMgb3ZlcmxhcHBpbmcgdyBFTkNPREUgYmxhY2tsaXN0CgpgYGB7cn0KYmxhY2tfbGlzdCA9IHJlYWQudGFibGUoIn4vYW5ub3RhdGlvbnMvaGczOC5ibGFja2xpc3QuYmVkLmd6IikKYmxhY2tfbGlzdC5nciA9IEdSYW5nZXMoCiAgYmxhY2tfbGlzdFssMV0sIAogIElSYW5nZXMoYmxhY2tfbGlzdFssMl0sIGJsYWNrX2xpc3RbLDNdKQopOwppZHkgPSBxdWVyeUhpdHMoCiAgZmluZE92ZXJsYXBzKHguc3BAZmVhdHVyZSwgYmxhY2tfbGlzdC5ncikKKTsKaWYobGVuZ3RoKGlkeSkgPiAwKXsKICB4LnNwID0geC5zcFssLWlkeSwgbWF0PSJibWF0Il07Cn07Cnguc3AKYGBgCgpFeGNsdWRlIGJhZCBjaHJvbW9zb21lcwpgYGB7cn0KY2hyLmV4Y2x1ZGUgPSBzZXFsZXZlbHMoeC5zcEBmZWF0dXJlKVtncmVwKCJyYW5kb218Y2hyTSIsIHNlcWxldmVscyh4LnNwQGZlYXR1cmUpKV0KaWR5ID0gZ3JlcChwYXN0ZShjaHIuZXhjbHVkZSwgY29sbGFwc2U9InwiKSwgeC5zcEBmZWF0dXJlKQppZihsZW5ndGgoaWR5KSA+IDApewogIHguc3AgPSB4LnNwWywtaWR5LCBtYXQ9ImJtYXQiXQp9Cnguc3AKYGBgCgpyZW1vdmUgdGhlIHRvcCA1JSBiaW5zIHRoYXQgb3ZlcmxhcCB3aXRoIGludmFyaWFudCBmZWF0dXJlcyBzdWNoIGFzIHRoZSBob3VzZSBrZWVwaW5nIGdlbmUgcHJvbW90ZXJzCgpgYGB7cn0KYmluLmNvdiA9IGxvZzEwKE1hdHJpeDo6Y29sU3Vtcyh4LnNwQGJtYXQpKzEpCiMgYmluLmNvdiA9IE1hdHJpeDo6Y29sU3Vtcyh4LnNwQGJtYXQpCmhpc3QoCiAgYmluLmNvdltiaW4uY292ID4gMF0sIAogIHhsYWI9ImxvZzEwKGJpbiBjb3YpIiwgCiAgbWFpbj0ibG9nMTAoQmluIENvdikiLCAKICBjb2w9ImxpZ2h0Ymx1ZSIsIAogICMgeGxpbT1jKDAsIDUpLAogIGJyZWFrcz0xMDAKKTsKYmluLmN1dG9mZiA9IHF1YW50aWxlKGJpbi5jb3ZbYmluLmNvdiA+IDBdLCAwLjk1KQppZHkgPSB3aGljaChiaW4uY292IDw9IGJpbi5jdXRvZmYgJiBiaW4uY292ID4gMCkKeC5zcCA9IHguc3BbLCBpZHksIG1hdD0iYm1hdCJdOwp4LnNwCmBgYAoKUmVtb3ZlIGFueSBjZWxscyBvZiBiaW4gY292ZXJhZ2UgbGVzcyB0aGFuIDEsMDAwLiBUaGUgcmF0aW9uYWwgYmVoaW5kIHRoaXMgaXMgdGhhdCBzb21lIGNlbGxzIG1heSBoYXZlIGhpZ2ggbnVtYmVyIG9mIHVuaXF1ZSBmcmFnbWVudHMgYnV0IGVuZCB1cCB3aXRoIGxvdyBiaW4gY292ZXJhZ2UgYWZ0ZXIgZmlsdGVyaW5nLiBUaGlzIHN0ZXAgaXMgb3B0aW9uYWwgYnV0IGhpZ2hseSByZWNvbW1hbmRlZC4KYGBge3J9CmlkeCA9IHdoaWNoKE1hdHJpeDo6cm93U3Vtcyh4LnNwQGJtYXQpID4gMTAwMCk7Cnguc3AgPSB4LnNwW2lkeCxdOwp4LnNwCmBgYAoKIyMgRGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uClVzZXMgZGlmZnVzaW9uIG1hcCBhbGdvcml0aG0gdyBzYW1wbGluZyB0ZWNobmlxdWUgdG8gbWFrZSBpdCBmYXN0LgoKYGBge3J9CiMjIFNhbXBsZSAxMDAgY2VsbHMgYXMgbGFuZG1hcmtzIApyb3cuY292cy5kZW5zIDwtIGRlbnNpdHkoCiAgeCA9IHguc3BAbWV0YURhdGFbLCJsb2dVTUkiXSwgCiAgYncgPSAnbnJkJywgYWRqdXN0ID0gMQopCnNhbXBsaW5nX3Byb2IgPC0gMSAvIChhcHByb3goeCA9IHJvdy5jb3ZzLmRlbnMkeCwgeSA9IHJvdy5jb3ZzLmRlbnMkeSwgeG91dCA9IHguc3BAbWV0YURhdGFbLCJsb2dVTUkiXSkkeSArIC5NYWNoaW5lJGRvdWJsZS5lcHMpOwpzZXQuc2VlZCgxKQppZHgubGFuZG1hcmsuZHMgPC0gc29ydChzYW1wbGUoeCA9IHNlcShucm93KHguc3ApKSwgc2l6ZSA9IDEwMDAsIHByb2IgPSBzYW1wbGluZ19wcm9iKSkKCiMjIFNwbGl0IGJldHdlZW4gbGFuZG1hcmsgYW5kIHF1ZXJ5IGNlbGxzCngubGFuZG1hcmsuc3AgPSB4LnNwW2lkeC5sYW5kbWFyay5kcyxdOwp4LnF1ZXJ5LnNwID0geC5zcFstaWR4LmxhbmRtYXJrLmRzLF07CgojIyBSdW4gZGlmZnVzaW9uIG1hcCBvbiBsYW5kbWFyawp4LmxhbmRtYXJrLnNwID0gcnVuRGlmZnVzaW9uTWFwcygKICBvYmo9IHgubGFuZG1hcmsuc3AsCiAgaW5wdXQubWF0PSJibWF0IiwgCiAgbnVtLmVpZ3M9NTAKKTsKeC5sYW5kbWFyay5zcEBtZXRhRGF0YSRsYW5kbWFyayA9IDE7CgojIyBQcm9qZWN0IHF1ZXJ5IGNlbGxzCngucXVlcnkuc3AgPSBydW5EaWZmdXNpb25NYXBzRXh0ZW5zaW9uKAogIG9iajE9eC5sYW5kbWFyay5zcCwgCiAgb2JqMj14LnF1ZXJ5LnNwLAogIGlucHV0Lm1hdD0iYm1hdCIKKQp4LnF1ZXJ5LnNwQG1ldGFEYXRhJGxhbmRtYXJrID0gMDsKCiMjIENvbWJpbmUgCnguc3AgPSBzbmFwUmJpbmQoeC5sYW5kbWFyay5zcCwgeC5xdWVyeS5zcCk7Cnguc3AgPSB4LnNwW29yZGVyKHguc3BAbWV0YURhdGFbLCJzYW1wbGUiXSldOyAjSU1QT1JUQU5UCmBgYAoKVG8gZGV0ZXJtaW5lIHNpZ25pZmljYW50IGRpZmZ1c2lvbiBjb21wb25lbnRzOgo+IFdlIHVzZSBhbiBhZCBob2MgbWV0aG9kIGJ5IHNpbXBseSBsb29raW5nIGF0IGEgcGFpcndpc2UgcGxvdCBhbmQgc2VsZWN0IHRoZSBudW1iZXIgb2YgZWlnZW4gdmVjdG9ycyB0aGF0IHRoZSBzY2F0dGVyIHBsb3Qgc3RhcnRzIGxvb2tpbmcgbGlrZSBhIGJsb2IuIEluIHRoZSBiZWxvdyBleGFtcGxlLCB3ZSBjaG9vc2UgdGhlIGZpcnN0IDE1IGVpZ2VuIHZlY3RvcnMuCgpgYGB7cn0KcGxvdERpbVJlZHVjdFBXKAogICAgb2JqPXguc3AsIAogICAgZWlncy5kaW1zPTE6NTAsCiAgICBwb2ludC5zaXplPTAuMywKICAgIHBvaW50LmNvbG9yPSJncmV5IiwKICAgIHBvaW50LnNoYXBlPTE5LAogICAgcG9pbnQuYWxwaGE9MC42LAogICAgZG93bi5zYW1wbGU9NTAwMCwKICAgIHBkZi5maWxlLm5hbWU9TlVMTCwgCiAgICBwZGYuaGVpZ2h0PTcsIAogICAgcGRmLndpZHRoPTcKICApOwpgYGAKCiMjIENsdXN0ZXJpbmcgYW5kIHZpc3VhbGl6YXRpb24KYGBge3J9CnNpZ25pZi5kaW1zID0gMToxNgp4LnNwID0gcnVuS05OKAogICAgb2JqPXguc3AsCiAgICBlaWdzLmRpbXM9c2lnbmlmLmRpbXMsCiAgICBrPTE1CiAgKTsKCnguc3A9cnVuQ2x1c3RlcigKICAgIG9iaj14LnNwLAogICAgdG1wLmZvbGRlcj10ZW1wZGlyKCksCiAgICBsb3V2YWluLmxpYj0ibGVpZGVuIiwKICAgIHNlZWQudXNlPTEwLAogICAgcmVzb2x1dGlvbj0wLjcKICApOwpgYGAKClZpc3VhbGl6YXRpb24KYGBge3IsIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTV9Cnguc3AgPSBydW5WaXooCiBvYmo9eC5zcCwgCiB0bXAuZm9sZGVyPXRlbXBkaXIoKSwKIGRpbXM9MiwKIGVpZ3MuZGltcz1zaWduaWYuZGltcywgCiBtZXRob2Q9InVtYXAiLAogc2VlZC51c2U9MTAKKQoKcGxvdFZpeigKICAgIG9iaj0geC5zcCwKICAgIG1ldGhvZD0idW1hcCIsIAogICAgbWFpbj0iQ2x1c3RlciIsCiAgICBwb2ludC5jb2xvcj14LnNwQGNsdXN0ZXIsIAogICAgcG9pbnQuc2l6ZT0wLjIsIAogICAgcG9pbnQuc2hhcGU9MTksIAogICAgdGV4dC5hZGQ9VFJVRSwKICAgIHRleHQuc2l6ZT0xLAogICAgdGV4dC5jb2xvcj0iYmxhY2siLAogICAgZG93bi5zYW1wbGU9MTAwMDAsCiAgICBsZWdlbmQuYWRkPUZBTFNFCiAgKTsKCmBgYAoKCjwhLS0gIyMgT3V0bGllciBhbmFseXNpcyAtLT4KPCEtLSBgYGB7cn0gLS0+CjwhLS0gZGF0YS5mcmFtZSh4LnNwQHVtYXApICU+JSAtLT4KPCEtLSAgIG11dGF0ZShjZWxsPXguc3BAYmFyY29kZSkgJT4lIC0tPgo8IS0tICAgIyBhcnJhbmdlKHVtYXAuMSkgLS0+CjwhLS0gICBnZ3Bsb3QoYWVzKHVtYXAuMSwgdW1hcC4yKSkgKyAtLT4KPCEtLSAgIGdlb21fcG9pbnQoc2l6ZT0wLjIpIC0tPgo8IS0tIGBgYCAtLT4KPCEtLSBgYGB7ciwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9MTV9IC0tPgo8IS0tIG91dGxpZXIuaXguMSA8LSBkYXRhLmZyYW1lKHguc3BAdW1hcCkgJT4lIC0tPgo8IS0tICAgbXV0YXRlKGNlbGw9eC5zcEBiYXJjb2RlKSAlPiUgLS0+CjwhLS0gICByb3dpZF90b19jb2x1bW4oKSAlPiUgLS0+CjwhLS0gICBkcGx5cjo6ZmlsdGVyKHVtYXAuMSA+IDEwMCkgJT4lIC0tPgo8IS0tICAgcHVsbChyb3dpZCkgLS0+CjwhLS0gb3V0bGllci5peC4yIDwtIGRhdGEuZnJhbWUoeC5zcEB1bWFwKSAlPiUgLS0+CjwhLS0gICBtdXRhdGUoY2VsbD14LnNwQGJhcmNvZGUpICU+JSAtLT4KPCEtLSAgIHJvd2lkX3RvX2NvbHVtbigpICU+JSAtLT4KPCEtLSAgIGRwbHlyOjpmaWx0ZXIodW1hcC4yIDwgLTUwKSAlPiUgLS0+CjwhLS0gICBwdWxsKHJvd2lkKSAtLT4KCjwhLS0gc21wLm90aGVyIDwtIHNhbXBsZShzZXRkaWZmKDE6bGVuZ3RoKHguc3BAYmFyY29kZSksIGMob3V0bGllci5peC4xLCBvdXRsaWVyLml4LjIpKSwgMTAwKSAtLT4KCjwhLS0gbG9uZy5vdXRsaWVyLmJtYXQgPC0gLS0+CjwhLS0gICB4LnNwQGJtYXRbYyhvdXRsaWVyLml4LjEsIG91dGxpZXIuaXguMiwgc21wLm90aGVyKSxdICU+JSAtLT4KPCEtLSAgIGFzLm1hdHJpeCgpICU+JSAtLT4KPCEtLSAgIG1lbHQodmFsdWUubmFtZSA9ICJhY2MiLCB2YXJuYW1lcz1jKCJiYXJjb2RlIiwgImJpbiIpKSAtLT4KCjwhLS0gIyBsb25nLm91dGxpZXIuYm1hdCAlPiUgLS0+CjwhLS0gIyAgICMgZHBseXI6OmZpbHRlcihiYXJjb2RlPT0nQUFDQUFBR0dUQUdBQ0dDQS0xJykgJT4lIC0tPgo8IS0tICMgICBtdXRhdGUoYmlnLmJpbnM9Y3V0KGJpbixicmVha3MgPSAxMDAwMCkpICU+JSAtLT4KPCEtLSAjICAgZ3JvdXBfYnkoYmFyY29kZSkgJT4lIC0tPgo8IS0tICMgICBhcnJhbmdlKGJpbikgJT4lIC0tPgo8IS0tICMgICBtdXRhdGUocnVubWVhbj1yb2xsbWVhbihhY2MsayA9IDEwMDAwKVsxOm4oKV0pICU+JSAtLT4KPCEtLSAjICAgdW5ncm91cCgpICU+JSAtLT4KPCEtLSAjICAgZ3JvdXBfYnkoYmFyY29kZSwgYmlnLmJpbnMpICU+JSAtLT4KPCEtLSAjICAgc3VtbWFyaXNlKGJpbi5tZWFuPW1lYW4ocnVubWVhbikpICU+JSAtLT4KPCEtLSAjICAgbXV0YXRlKGJpZy5iaW5zPWFzLm51bWVyaWMoYmlnLmJpbnMpKSAlPiUgLS0+CjwhLS0gIyAgIG11dGF0ZShvdXRsaWVyPWlmZWxzZShiYXJjb2RlICVpbiUgeC5zcEBiYXJjb2RlW291dGxpZXIuaXhdLCBUUlVFLCBGQUxTRSkpICU+JSAtLT4KPCEtLSAjICAgZ2dwbG90KGFlcyggYmlnLmJpbnMsIGJhcmNvZGUsIGZpbGw9YmluLm1lYW4pKSArIC0tPgo8IS0tICMgICBnZW9tX3RpbGUoKSArIC0tPgo8IS0tICMgICBzY2FsZV9maWxsX3ZpcmlkaXNfYygpICsgLS0+CjwhLS0gIyAgIGZhY2V0X3dyYXAob3V0bGllcn4uLCBzY2FsZXM9ImZyZWVfeSIsIG5jb2w9MSwgbnJvdz0yKSAtLT4KPCEtLSAjICAgdW5ncm91cCgpICU+JSAtLT4KPCEtLSAjICAgZ2dwbG90KGFlcyhiaW4sIHJ1bm1lYW4pKSArIC0tPgo8IS0tICMgICBmYWNldF93cmFwKGJpZy5iaW5zfi4sIHNjYWxlPSJmcmVlX3giLCBuY29sPTIpICsgLS0+CjwhLS0gIyAgIGdlb21fbGluZShhZXMoZ3JvdXA9YmFyY29kZSwgY29sb3I9b3V0bGllciksIHNpemU9MC41LCBhbHBoYT0wLjUpIC0tPgo8IS0tICMgICAgLS0+CjwhLS0gYGBgIC0tPgoKPCEtLSBgYGB7cn0gLS0+CjwhLS0gbG9uZy5vdXRsaWVyLmJtYXQgJT4lIC0tPgo8IS0tICAgZ3JvdXBfYnkoYmFyY29kZSkgJT4lIC0tPgo8IS0tICAgc3VtbWFyaXNlKGFjYz1zdW0oYWNjKSkgJT4lIC0tPgo8IS0tICAgbXV0YXRlKG91dGxpZXI9Y2FzZV93aGVuKGJhcmNvZGUgJWluJSB4LnNwQGJhcmNvZGVbb3V0bGllci5peC4xXSB+ICAib3V0MSIsICAtLT4KPCEtLSAgICAgICAgICAgICAgICAgICAgICAgICAgICBiYXJjb2RlICVpbiUgeC5zcEBiYXJjb2RlW291dGxpZXIuaXguMl0gfiAib3V0MiIsIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICAgICAgIEZBTFNFIH4gJ290aGVyJykpICU+JSAtLT4KPCEtLSAgICMgbXV0YXRlKG91dGxpZXI9aWZlbHNlKGJhcmNvZGUgJWluJSB4LnNwQGJhcmNvZGVbb3V0bGllci5peC4yXSwgIm91dDIiLCBGQUxTRSkpICU+JSAtLT4KPCEtLSAgIGdncGxvdChhZXMob3V0bGllciwgYWNjKSkgKyAtLT4KPCEtLSAgIGdnYmVlc3dhcm06Omdlb21fcXVhc2lyYW5kb20oKSAtLT4KPCEtLSBgYGAgLS0+CgoKIyMgTWFrZSBnZW5lIG1hdHJpeApgYGB7cn0KdHJhbnNjcmlwdHMuZ3IgPSBydHJhY2tsYXllcjo6aW1wb3J0KCJ+L0hvbW9fc2FwaWVucy5HUkNoMzguOTMuZmlsdGVyZWQuZ3RmIikKY29sbmFtZXModHJhbnNjcmlwdHMuZ3JAZWxlbWVudE1ldGFkYXRhKSA8LSBzdHJfcmVwbGFjZShjb2xuYW1lcyh0cmFuc2NyaXB0cy5nckBlbGVtZW50TWV0YWRhdGEpLCAiZ2VuZV9uYW1lIiwgIm5hbWUiKQoKZ2VuZXMuZ3IgPC0gdW5saXN0KHJhbmdlKHNwbGl0KHRyYW5zY3JpcHRzLmdyLCB+IG5hbWUpKSkgICMjIEZyb20gdHJhbnNjcmlwdHMgdG8gZ2VuZXMKZ2VuZXMuZ3IkbmFtZSA8LSBuYW1lcyhnZW5lcy5ncikKCmlmIChHZW5vbWVJbmZvRGI6OnNlcWxldmVsc1N0eWxlKGdlbmVzLmdyKSAhPSBHZW5vbWVJbmZvRGI6OnNlcWxldmVsc1N0eWxlKHguc3BAZmVhdHVyZSkgKSB7CiAgR2Vub21lSW5mb0RiOjpzZXFsZXZlbHNTdHlsZShnZW5lcy5ncikgPC0gR2Vub21lSW5mb0RiOjpzZXFsZXZlbHNTdHlsZSh4LnNwQGZlYXR1cmUpCn0KCnguc3AgPSBjcmVhdGVHbWF0RnJvbU1hdCgKICAgIG9iaj14LnNwLCAKICAgIGlucHV0Lm1hdD0iYm1hdCIsCiAgICBnZW5lcz1nZW5lcy5nciwKICAgIGRvLnBhcj1UUlVFLAogICAgbnVtLmNvcmVzPTEwCiAgKQoKc2F2ZVJEUyh4LnNwLCBmaWxlID0gIn4vbXlfZGF0YS9jZWxscmFuZ2VyLWF0YWMxMTBfY291bnRfMzA0MzlfV1NTUzgwMzgzNjBfR1JDaDM4LTFfMV8wLnNuYXBBVEFDLlJEUyIpCgpgYGAKClJlcGVhdCB2aXN1YWxpemF0aW9uIG9uIGdtYXQKYGBge3J9CiMjIE1ha2Ugc3VlcmF0IG9iamVjdApmNzQuc2V1IDwtIHNuYXBUb1NldXJhdCgKICAgIG9iaj14LnNwLCAKICAgIGVpZ3MuZGltcz0xOjIwLCAKICAgIG5vcm09VFJVRSwKICAgIHNjYWxlPVRSVUUKICAgICkKCiMjIFNhdmUgVU1BUCBiYXNlZCBvbiBTbmFwQVRBQyBwcm9jZXNzaW5nCmY3NC5zZXUgPC0gUnVuVU1BUChmNzQuc2V1LCByZWR1Y3Rpb24gPSAiU25hcEFUQUMiLCBkaW1zPXNpZ25pZi5kaW1zLCByZWR1Y3Rpb24ubmFtZSA9ICJ1bWFwLnNuYXAiKQoKCmY3NC5zZXUgPC0gRmluZFZhcmlhYmxlRmVhdHVyZXMoZjc0LnNldSkKZjc0LnNldSA8LSBSdW5QQ0EoZjc0LnNldSwgZGltcz0xOjUwKQpmNzQuc2V1IDwtIFJ1blVNQVAoZjc0LnNldSwgcmVkdWN0aW9uID0gInBjYSIsIGRpbXM9MTo1MCwgdmVyYm9zZT1GQUxTRSkgCgpnZ3B1YnI6OmdnYXJyYW5nZSgKICBEaW1QbG90KGY3NC5zZXUsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSBOVUxMKSArIGdndGl0bGUoImdtYXQiKSwKICBEaW1QbG90KGY3NC5zZXUsIHJlZHVjdGlvbiA9ICJ1bWFwLnNuYXAiLCBncm91cC5ieSA9IE5VTEwpICsgZ2d0aXRsZSgiYm1hdCAtIHNuYXAiKSAKICApCgpgYGAKCgpgYGB7cn0KcGxvdGx5OjpnZ3Bsb3RseShEaW1QbG90KGY3NC5zZXUsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSBOVUxMKSArIGdndGl0bGUoImdtYXQiKSkKYGBgCgpgYGB7ciwgZmlnLmhlaWdodD0xNSwgZmlnLndpZHRoPTE1fQpGZWF0dXJlUGxvdChmNzQuc2V1LCBmZWF0dXJlcyA9IGMoJ1BUUFJDJywnQ0Q0JywnQ0Q4QScsJ0NEOEInLCdDRDc5QScsJ0ZPWE4xJywnRVBDQU0nLCdQREdGUkEnLCdHTkc0JywgJ0ZPWFAzJywnUkFHMScsJ1JBRzInLCdOS0c3JywnQ0NSNycpLCByZWR1Y3Rpb24gPSAidW1hcC5zbmFwIikKYGBgCgpgYGB7ciwgZmlnLmhlaWdodD0xNSwgZmlnLndpZHRoPTE1fQpEaW1IZWF0bWFwKGY3NC5zZXUsIGRpbXMgPSAxOjYsIGJhbGFuY2VkID0gVFJVRSxuY29sID0gMSkKYGBgCgojIyMgRGlmZmVyZW50aWFsIGFuYWx5c2lzIAo8IS0tIFNuYXAgQVRBQyB3YXkgLS0+CjwhLS0gYGBge3J9IC0tPgo8IS0tIGNsdXN0ZXIgPSAxIC0tPgo8IS0tIERBUnM9IGZpbmREQVIoIC0tPgo8IS0tICAgICBvYmo9eC5zcCwgLS0+CjwhLS0gICAgIGlucHV0Lm1hdD0iZ21hdCIsIC0tPgo8IS0tICAgICBjbHVzdGVyLnBvcz1jbHVzdGVyLCAtLT4KPCEtLSAgICAgIyBjbHVzdGVyLm5lZyA9IDUsIC0tPgo8IS0tICAgICBjbHVzdGVyLm5lZy5tZXRob2Q9InJhbmRvbSIsIC0tPgo8IS0tICAgICB0ZXN0Lm1ldGhvZD0iZXhhY3RUZXN0IiwgLS0+CjwhLS0gICAgIGJjdj0wLjQsICMwLjQgZm9yIGh1bWFuLCAwLjEgZm9yIG1vdXNlIC0tPgo8IS0tICAgICBzZWVkLnVzZT00MiAtLT4KPCEtLSAgICk7IC0tPgoKCjwhLS0gZ21hdC5jbCA8LSB4LnNwQGdtYXRbd2hpY2goeC5zcEBjbHVzdGVyPT1jbHVzdGVyKSxdIC0tPgo8IS0tIGdtYXQubmVnIDwtIHguc3BAZ21hdFt3aGljaCh4LnNwQGNsdXN0ZXIhPWNsdXN0ZXIpLF0gLS0+Cgo8IS0tIGhpc3QoYXMubWF0cml4KGdtYXQuY2wpLCBicmVha3M9NTApIC0tPgoKCjwhLS0gIyBEQVJzICU+JSAtLT4KPCEtLSAgICMgbXV0YXRlKGdlbmU9Y29sbmFtZXMoeC5zcEBnbWF0KSkgJT4lIC0tPgo8IS0tICAgIyBhcnJhbmdlKCkgJT4lIC0tPgo8IS0tICAgIyBmaWx0ZXIoUFZhbHVlIDwgMC4wNSkgLS0+Cgo8IS0tIGhpc3QoREFScyRQVmFsdWUsIGJyZWFrcyA9IDIwKSAtLT4KCjwhLS0gREFScyRGRFIgPSBwLmFkanVzdChEQVJzJFBWYWx1ZSwgbWV0aG9kPSJCSCIpIC0tPgo8IS0tIGlkeSA9IHdoaWNoKERBUnMkRkRSIDwgNWUtMiAmIERBUnMkbG9nRkMgPiAwKSAtLT4KPCEtLSBwbG90KERBUnMkbG9nQ1BNLCBEQVJzJGxvZ0ZDLCAgLS0+CjwhLS0gICAgIHBjaD0xOSwgY2V4PTAuMSwgY29sPSJncmV5IiwgIC0tPgo8IS0tICAgICB5bGFiPSJsb2dGQyIsIHhsYWI9ImxvZ0NQTSIsIC0tPgo8IS0tICAgICBtYWluPXBhc3RlKCJDbHVzdGVyIiwgY2x1c3RlcikgLS0+CjwhLS0gICApIC0tPgo8IS0tIHBvaW50cyhEQVJzJGxvZ0NQTVtpZHldLCAgLS0+CjwhLS0gICAgIERBUnMkbG9nRkNbaWR5XSwgIC0tPgo8IS0tICAgICBwY2g9MTksICAtLT4KPCEtLSAgICAgY2V4PTAuNSwgIC0tPgo8IS0tICAgICBjb2w9InJlZCIgLS0+CjwhLS0gICApIC0tPgoKPCEtLSBgYGAgLS0+CmBgYHtyfQpEQUdzX2NsIDwtIEZpbmRBbGxNYXJrZXJzKGY3NC5zZXUsIG1pbi5wY3QgPSAwLjIsIGxvZ2ZjLnRocmVzaG9sZCA9IDAuMykKREFHc19jbCA8LSAKICBEQUdzX2NsICU+JQogIG11dGF0ZShwb3NpdGl2ZT1hdmdfbG9nRkMgPiAwKQpgYGAKYGBge3IsIGZpZy5oZWlnaHQ9MTQsIGZpZy53aWR0aD05fQp0b3AxMF9tYXJrZXJzIDwtIERBR3NfY2wgJT4lCiAgZmlsdGVyKHBvc2l0aXZlKSAlPiUKICBncm91cF9ieShjbHVzdGVyKSAlPiUKICBhcnJhbmdlKHBfdmFsX2FkaikgJT4lCiAgdG9wX24oMTApICU+JQogIHB1bGwoZ2VuZSkgJT4lCiAgdW5pcXVlKCkKCnRvcDEwX21hcmtlcnMgPC0gREFHc19jbCAlPiUKICBmaWx0ZXIocG9zaXRpdmUpICU+JQogIGdyb3VwX2J5KGNsdXN0ZXIpICU+JQogICMgYXJyYW5nZShwX3ZhbF9hZGopICU+JQogIHRvcF9uKDEwLHd0ID0gLSBwX3ZhbF9hZGopICU+JQogIHB1bGwoZ2VuZSkKCkRvdFBsb3QoZjc0LnNldSwgZmVhdHVyZXMgPSB0b3AxMF9tYXJrZXJzKSArCiAgY29vcmRfZmxpcCgpCmBgYAoKRXhwcmVzc2lvbiBvZiBtYXJrZXJzIGluIFJOQSBkYXRhCgpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTE2fQpwbG90X21hcmtlcl9BVEFDdnNSTkEgPC0gZnVuY3Rpb24obWFya2VyKXsKICAgIGdncHVicjo6Z2dhcnJhbmdlKAogICAgICBGZWF0dXJlUGxvdChmNzQuc2V1LCBmZWF0dXJlPW1hcmtlciwgcmVkdWN0aW9uPSJ1bWFwLnNuYXAiKSArIGdndGl0bGUoIkFUQUMiKSwgRmVhdHVyZVBsb3QoZjc0LnJuYS5zZXUsIGZlYXR1cmU9bWFya2VyKSArIGdndGl0bGUoIlJOQSIpCiAgICApICU+JSBhbm5vdGF0ZV9maWd1cmUodG9wPW1hcmtlcikKICB9CgpwbG90X2NsdXN0ZXJfbWFya2VyX0FUQUN2c1JOQSA8LSBmdW5jdGlvbihjbCl7CiAgdG9wMTBfY2x1c3RlciA8LQogICAgREFHc19jbCAlPiUKICAgIGZpbHRlcihwb3NpdGl2ZSkgJT4lCiAgICBmaWx0ZXIoY2x1c3Rlcj09Y2wpICU+JQogICAgZmlsdGVyKGdlbmUgJWluJSByb3duYW1lcyhmNzQucm5hLnNldSkpICU+JQogICAgdG9wX24oMTAsd3QgPSAtIHBfdmFsX2FkaikgJT4lCiAgICBwdWxsKGdlbmUpCiAgZ2dhcnJhbmdlKHBsb3RsaXN0ID0gbWFwKHRvcDEwX2NsdXN0ZXIsIH4gcGxvdF9tYXJrZXJfQVRBQ3ZzUk5BKC54KSksIG5jb2w9MiwgbnJvdz01KSAlPiUgCiAgICBhbm5vdGF0ZV9maWd1cmUoZmlnLmxhYj1wYXN0ZSgiQ2x1c3RlciIsIGNsKSwgZmlnLmxhYi5zaXplID0gMjAsIGZpZy5sYWIuZmFjZSA9ICJib2xkIikKICB9CgptYXAoMToxMCwgfiBwbG90X2NsdXN0ZXJfbWFya2VyX0FUQUN2c1JOQSgueCkpCmBgYAoKYGBge3J9CnBsb3RseTo6Z2dwbG90bHkoRGltUGxvdChmNzQucm5hLnNldSwgZ3JvdXAuYnkgPSAiYW5ub3RhdGlvbiIpKQpgYGAKCgojIyBFbWJlZGRpbmcgb24gbW9zdCB2YXJpYWJsZSBnZW5lcyBpbiBBVEFDIChub3QgaW4gZ2VuZSBleHByZXNzaW9uKQpgYGB7cn0KRGVmYXVsdEFzc2F5KGY3NC5zZXUpIDwtICJBQ1RJVklUWSIKZjc0LnNldS5odmdhdGFjIDwtIEZpbmRWYXJpYWJsZUZlYXR1cmVzKGY3NC5zZXUpCmY3NC5zZXUuaHZnYXRhYyA8LSBSdW5QQ0EoZjc0LnNldS5odmdhdGFjLCBkaW1zPTE6MzApCmY3NC5zZXUuaHZnYXRhYyA8LSBSdW5VTUFQKGY3NC5zZXUuaHZnYXRhYywgcmVkdWN0aW9uID0gInBjYSIsIGRpbXM9MTozMCwgdmVyYm9zZT1GQUxTRSkgCgpmNzQuc2V1Lmh2Z2F0YWMgPC0gQWRkTWV0YURhdGEoZjc0LnNldS5odmdhdGFjLCB4LnNwQGNsdXN0ZXIsIGNvbC5uYW1lPSdTbmFwQVRBQ19jbHVzdGVyJykKRGltUGxvdChmNzQuc2V1Lmh2Z2F0YWMsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAiU25hcEFUQUNfY2x1c3RlciIpIApgYGAKCgojIyBUaG91Z2h0cwoKLSBUaGVyZSBzZWVtcyB0byBiZSBhIGNvcnJlc3BvbmRhbmNlIGZvciBnZW5lIGV4cHJlc3Npb24gYW5kIGFjY2Vzc2liaWxpdHkgaW4gbWFueSAiYWNjZXNzaWJpbGl0eSBtYXJrZXJzIiAoc2VlIENsdXN0ZXIgNikgLS0+IG1heWJlIGludGVncmF0aW9uIHdvdWxkIHdvcmsgYmV0dGVyIGlmIEkgZG9uJ3QgdGFrZSBvbmx5IHRoZSBtb3N0IHZhcmlhYmxlIGZlYXR1cmVzIGluIHRoZSBSTkEgZGF0YS4gCgoKCg==